package com.hd.trans.extension

import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.graphics.Matrix
import android.os.SystemClock
import android.text.Spanned
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.animation.addListener
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hd.trans.utils.HtmlCompat


@Deprecated("Use androidx.core.view.isVisible instead", ReplaceWith("isVisible", "androidx.core.view.isVisible"))
var View.visible: Boolean
    set(value) {
        visibility = if (value) View.VISIBLE else View.GONE
    }
    get() = visibility == View.VISIBLE

var View.layoutWidth:Int
    set(value) {
        layoutParams.width = value
        requestLayout()
    }
    get() = layoutParams.width

var View.layoutHeight:Int
    set(value) {
        layoutParams.height = value
        requestLayout()
    }
    get() = layoutParams.height

var View.leftMargin:Int
    set(value) {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                it.leftMargin = value
                requestLayout()
            }
        }
    }
    get() {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                return it.leftMargin
            }
        }
        return 0
    }

var View.rightMargin:Int
    set(value) {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                it.rightMargin = value
                requestLayout()
            }
        }
    }
    get() {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                return it.rightMargin
            }
        }
        return 0
    }

var View.topMargin:Int
    set(value) {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                it.topMargin = value
                requestLayout()
            }
        }
    }
    get() {
        layoutParams?.let {
            if (it is ViewGroup.MarginLayoutParams){
                return it.topMargin
            }
        }
        return 0
    }

fun TextView.setHtmlText(html:Int){
    text = HtmlCompat.fromHtml(context.getString(html))
}

var TextView.html:CharSequence
    get() = text
    set(value) {
        text = if (value is Spanned){
            value
        } else{
            HtmlCompat.fromHtml(value.toString())
        }
    }

var View.constraintWidth:Float
    get() {
        val params = layoutParams
        return if (params is ConstraintLayout.LayoutParams){
            params.matchConstraintPercentWidth
        } else{
            0f
        }
    }
    set(value) {
        val params = layoutParams
        if (params is ConstraintLayout.LayoutParams){
            params.matchConstraintPercentWidth = value
            requestLayout()
        }
    }

fun View.setPaddingBottom(value:Int){
    setPadding(paddingLeft,paddingTop,paddingRight,value)
}

fun View.setPaddingTop(value:Int){
    setPadding(paddingLeft,value,paddingRight,paddingBottom)
}

fun View.setPaddingLeft(value:Int){
    setPadding(value,paddingTop,paddingRight,paddingBottom)
}

fun View.setPaddingRight(value:Int){
    setPadding(paddingLeft,paddingTop,value,paddingBottom)
}

fun View.setPaddingHorizontal(value:Int){
    setPadding(value,paddingTop,value,paddingBottom)
}

fun View.setPaddingVertical(value:Int){
    setPadding(paddingLeft,value,paddingRight,value)
}

fun View.setPaddingAll(value:Int){
    setPadding(value,value,value,value)
}

fun View.getLocationLeftOnScreen():Int{
    val location = IntArray(2)
    getLocationOnScreen(location)
    return location[0]
}

fun View.getLocationRightOnScreen():Int{
    val location = IntArray(2)
    getLocationOnScreen(location)
    return location[0] + measuredWidth
}

fun View.getLocationTopOnScreen():Int{
    val location = IntArray(2)
    getLocationOnScreen(location)
    return location[1]
}

fun View.getLocationBottomOnScreen():Int{
    val location = IntArray(2)
    getLocationOnScreen(location)
    return location[1] + measuredHeight
}

fun View.getLocationLeftInWindow():Int{
    val location = IntArray(2)
    getLocationInWindow(location)
    return location[0]
}

fun View.getLocationTopInWindow():Int{
    val location = IntArray(2)
    getLocationInWindow(location)
    return location[1]
}

fun View.getLocationBottomInWindow():Int{
    val location = IntArray(2)
    getLocationInWindow(location)
    return location[1] + measuredHeight
}

fun View?.getMeasuredWidthNow():Int{
    if (this == null)
        return 0
    var width = measuredWidth
    if (width == 0){
        measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
        width = measuredWidth
    }
    return width
}

fun View?.getMeasuredHeightNow():Int{
    if (this == null)
        return 0
    var height = measuredHeight
    if (height == 0){
        measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
        height = measuredHeight
    }
    return height
}

var View.tagInt:Int
    set(value) {
        tag = value
    }
    get() {
        return tag as? Int ?: 0
    }

var View.tagLong:Long
    set(value) {
        tag = value
    }
    get() {
        val tagValue = tag
        return if (tagValue is Long) tagValue else 0L
    }

inline fun <reified T> View.tagAs():T?{
    val t = tag ?: return null
    return if (t is T) t else null
}

var View.textViewText:String
    set(value) {
        if (this is TextView){
            text = value
        }
    }
    get() {
        return if (this is TextView){
            text.toString()
        }
        else{
            ""
        }
    }


/**
 * 透明的渐变动画
 * @param destAlpha 目标透明度
 * @param duration 动画时长
 * @param block 动画结束回调
 */
fun View.alphaAnimation(destAlpha:Float,duration:Long = 300,block:((v:View)->Unit)? = null){
    val animator = ObjectAnimator.ofFloat(this, View.ALPHA.name, destAlpha).setDuration(duration)
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

/**
 * 缩放动画
 * @param destScale 目标缩放比例
 * @param duration 动画时长
 * @param block 动画结束回调
 */
fun View.scaleAnimation(destScale:Float,duration:Long = 300,block:((v:View)->Unit)? = null){
    val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X.name, this.scaleX, destScale)
    val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y.name, this.scaleY, destScale)
    val animator = AnimatorSet()
    animator.playTogether(scaleX,scaleY)
    animator.duration = duration
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

/**
 * 旋转动画
 * @param destRotation 目标旋转角度
 * @param pivotX 旋转中心点X坐标
 * @param pivotY 旋转中心点Y坐标
 * @param duration 动画时长
 * @param block 动画结束回调
 */
fun View.rotationAnimation(destRotation:Float,pivotX:Float = -1f,pivotY:Float = -1f,duration:Long = 300,block:((v:View)->Unit)? = null){
    val animator = ObjectAnimator.ofFloat(this, View.ROTATION.name, destRotation).setDuration(duration)
    if (pivotX >= 0){
        this.pivotX = pivotX * measuredWidth
    }
    if (pivotY >= 0){
        this.pivotY = pivotY * measuredHeight
    }
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

fun View.setAlphaVisible(visible:Boolean,duration:Long = 300){
    val fromAlpha:Float
    val destAlpha:Float

    if (visible){
        fromAlpha = 0f
        destAlpha = 1f
    }
    else{
        fromAlpha = 1f
        destAlpha = 0f
    }

    this.alpha = fromAlpha
    this.isVisible = true

    alphaAnimation(destAlpha,duration){
        isVisible = visible
    }
}

/**
 * 垂直方向平移动画
 * @param destY 目标Y坐标
 * @param duration 动画时长
 * @param block 动画结束回调
 */
fun View.translationYAnimation(destY:Float,duration:Long = 300,block:((v:View)->Unit)? = null){
    val animator = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, destY).setDuration(duration)
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

fun View.translationXAnimation(destX:Float,duration:Long = 300,block:((v:View)->Unit)? = null){
    val animator = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, destX).setDuration(duration)
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

fun View.widthAnimation(destWidth:Int,duration:Long = 300,block:((v:View)->Unit)? = null){
    val startWidth = layoutParams.width
    val animator = ValueAnimator.ofInt(startWidth,destWidth).setDuration(duration)
    animator.addUpdateListener {
        layoutParams.width = it.animatedValue as Int
        requestLayout()
    }
    if(block != null){
        animator.addListener(onEnd = {block.invoke(this)})
    }
    animator.start()
}

val Matrix.scaleX:Float
    get() {
        val values = FloatArray(9)
        getValues(values)
        return values[Matrix.MSCALE_X]
    }

val Matrix.scaleY:Float
    get() {
        val values = FloatArray(9)
        getValues(values)
        return values[Matrix.MSCALE_Y]
    }


fun View.setOnDebouncedClickListener(action: () -> Unit) {
    val actionDebouncer = ActionDebouncer(action)

    // This is the only place in the project where we should actually use setOnClickListener
    setOnClickListener {
        actionDebouncer.notifyAction()
    }
}

fun View.setOnDebouncedClickListeners(action: (View) -> Unit) {
    val actionDebouncer = ActionDebouncer(action)
    setOnClickListener {
        actionDebouncer.notifyAction(it)
    }
}

fun View.removeOnDebouncedClickListener() {
    setOnClickListener(null)
    isClickable = false
}

fun RecyclerView.scrollToLastItem(){
    val linearLayoutManager = layoutManager
    if (linearLayoutManager == null || adapter == null)
        return
    linearLayoutManager as LinearLayoutManager
    val lastItemPosition = linearLayoutManager.itemCount - 1
    linearLayoutManager.scrollToPosition(lastItemPosition)
}

fun RecyclerView.scrollToBottom(){
    val linearLayoutManager = layoutManager
    if (linearLayoutManager == null || adapter == null)
        return
    linearLayoutManager as LinearLayoutManager
    val lastItemPosition = linearLayoutManager.itemCount - 1
    val lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition()
    if (lastVisibleItemPosition == lastItemPosition) {
        //如果已滚动到最后一个item
        linearLayoutManager.findViewByPosition(lastItemPosition)?.let {
            val offset: Int = measuredHeight - it.measuredHeight
            linearLayoutManager.scrollToPositionWithOffset(lastItemPosition, offset)
        }
    }
    else{
        //先滚动到最后一个item
        linearLayoutManager.scrollToPositionWithOffset(lastItemPosition, 0)
        post(Runnable {
            //再滚动偏移量
            linearLayoutManager.findViewByPosition(lastItemPosition)?.let {
                val offset: Int = measuredHeight - it.measuredHeight
                linearLayoutManager.scrollToPositionWithOffset(lastItemPosition, offset)
            }
        })

    }
}

fun RecyclerView?.isScrolledToBottom():Boolean{
    if (this == null)
        return false
    return this.computeVerticalScrollExtent() + this.computeVerticalScrollOffset() >= this.computeVerticalScrollRange()
}

fun RecyclerView.Adapter<*>?.notifyItemsChanged(vararg positions:Int){
    if (this == null) return
    val set = hashSetOf<Int>()
    positions.forEach {
        if (it >= 0 && !set.contains(it)){
            set.add(it)
            notifyItemChanged(it)
        }
    }
}

private class ActionDebouncer {
    private val action1: (() -> Unit)?
    private val action2: ((View) -> Unit)?

    constructor(action: () -> Unit) {
        this.action1 = action
        this.action2 = null
    }

    constructor(action: (View) -> Unit) {
        this.action1 = null
        this.action2 = action
    }

    companion object {
        const val DEBOUNCE_INTERVAL_MILLISECONDS = 600L
    }

    private var lastActionTime = 0L

    fun notifyAction(v:View? = null) {
        val now = SystemClock.elapsedRealtime()
        val millisecondsPassed = now - lastActionTime
        val actionAllowed = millisecondsPassed > DEBOUNCE_INTERVAL_MILLISECONDS
        lastActionTime = now

        if (actionAllowed) {
            action1?.invoke()
            v?.let {
                action2?.invoke(v)
            }
        }
    }
}

fun SeekBar.doOnSeekBarChangeFromUser(onSeekBarChange:(progress: Int)->Unit){
    setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
        override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
            if(fromUser){
                onSeekBarChange.invoke(progress)
            }
        }

        override fun onStartTrackingTouch(seekBar: SeekBar?) {
        }

        override fun onStopTrackingTouch(seekBar: SeekBar?) {
        }

    })
}
